from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtWidgets import (QMainWindow, QApplication, QComboBox,QDesktopWidget,QPushButton,QLabel,QLineEdit, QFileDialog)
from PyQt5.QtGui import QIcon,QPalette,QColor,QFont
from PyQt5.QtCore import Qt
import sys
import json
import os
import serial
import serial.tools.list_ports
import esptool
import threading
from PIL import Image
import cv2
import numpy as np




font = QFont()
font.setFamily("YouYuan")
font.setStyleName("Bold")

def clear_flash_fun(command, status):
    try: 
        esptool.main(command)
        status.showMessage("清除成功！")
    except Exception as e:
        print(e)
        status.showMessage("清除失败!")

def burn_flash_fun(command, status):
    try:
        esptool.main(command)
        status.showMessage("烧录成功!")
    except Exception as e:
        print(e)
        status.showMessage("烧录失败!")

def reboot_fun(command, status):
    try:
        esptool.main(command)
        status.showMessage("重启成功!")
    except Exception as e:
        print(e)
        status.showMessage("重启失败!")

def avi2pokiani(file_in_path, file_out_path):
    with open(file_out_path, 'wb') as fout:
        with open(file_in_path, 'rb') as fin:
            file_byte = fin.read().split(b'\x6d\x6f\x76\x69')[1].split(b'\x69\x64\x78\x31')[0] #start with "movi", end with idx1
            frame_byte_list = file_byte.split(b'\xff\xd8')
            file_out = b''
            frame_len = len(frame_byte_list)
            for i, frame_byte in enumerate(frame_byte_list):
                if i>0:
                    len_frame = len(frame_byte)+2
                    len_frame_hex = len_frame.to_bytes(length=4, byteorder='little', signed=False)
                    # file_out = file_out + len_frame_hex + b'\xff\xd8' + frame_byte
                    fout.write(len_frame_hex + b'\xff\xd8' + frame_byte)
                    if i%300==0:
                        print(int(i/frame_len*10+90), '%')
            print(100, '%')

def gif2avi(path_in, file_in, path_out, file_out):

    fps = 20
    img = Image.open(path_in+file_in)
    fourcc = cv2.VideoWriter_fourcc(*'MJPG')
    videoWriter = cv2.VideoWriter(path_out+file_out,fourcc,fps, (240, 240))#最后一个是保存图片的尺寸

    for i in range(img.n_frames):
        if i%4!=0:
            img.seek(i)
            frame = Image.new("RGB", img.size)
            frame.paste(img)
            frame = np.asarray(frame)
            frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
            frame = cv2.resize(frame, dsize=(240, 240), interpolation=cv2.INTER_LINEAR)
            videoWriter.write(frame)
    videoWriter.release()

def mp42avi(path_in, file_in, path_out, file_out):

    fps = 20
    videoCapture = cv2.VideoCapture(path_in+file_in)
    fourcc = cv2.VideoWriter_fourcc(*'MJPG')
    videoWriter = cv2.VideoWriter(path_out+file_out,fourcc,fps, (240, 240))#最后一个是保存图片的尺寸
    success, frame = videoCapture.read()
    frame_count = videoCapture.get(cv2.CAP_PROP_FRAME_COUNT)
    frame_no = 0
    while success :
        if frame_no%4!=0:
            frame = cv2.resize(np.asarray(frame), dsize=(240, 240), interpolation=cv2.INTER_LINEAR)
            videoWriter.write(frame)
        success, frame = videoCapture.read()
        frame_no+=1
        if frame_no%400==0:
            print(int(frame_no/frame_count*90), '%')
    print(int(frame_no/frame_count*90), '%')
    videoWriter.release()

def trans_gif(ori_file, path_out, status):
    status.showMessage("转换中...")
    try:
        pos = ori_file.rfind('/')
        path = ori_file[0:pos+1]
        file_name = ori_file[pos+1:]
        path_out = path_out+"/"

        gif2avi(path, file_name, path_out, file_name.replace('gif', 'avi'))
        avi2pokiani(path_out + file_name.replace('gif', 'avi'), path_out + file_name.replace('gif', 'pokani'))
        os.remove(path_out+file_name.replace('gif', 'avi'))
        status.showMessage("转换成功!")
    except Exception as e:
        print(e)
        status.showMessage("转换失败")

def trans_mp4(ori_file, path_out, status):
    status.showMessage("转换中...")
    try:
        pos = ori_file.rfind('/')
        path = ori_file[0:pos+1]
        file_name = ori_file[pos+1:]
        path_out = path_out+"/"

        mp42avi(path, file_name, path_out, file_name.replace('mp4', 'avi'))
        avi2pokiani(path_out + file_name.replace('mp4', 'avi'), path_out + file_name.replace('mp4', 'pokani'))
        os.remove(path_out+file_name.replace('mp4', 'avi'))
        status.showMessage("转换成功!")
    except Exception as e:
        print(e)
        status.showMessage("转换失败")

def screen_center(window,width,height):
    window.resize(width,height)
    screen_size=QDesktopWidget().screenGeometry()
    window.move(int((screen_size.width()-width)/2),int((screen_size.height()-height)/2))
#set the window color

def set_color(window,R=245,G=222,B=179):
    palette = QPalette()
    palette.setColor(window.backgroundRole(), QColor(R,G,B))
    window.setPalette(palette)

def add_label_line(window, scale_rate, x_pos=23, y_pos=16, label_text='', line_text=''):
    label=QLabel(window)
    label.setText(label_text)
    label.setStyleSheet("font:15pt")
    label.setFont(font)
    label.resize(label.sizeHint())
    label.move(int(x_pos*scale_rate), int(y_pos*scale_rate))
    line=QLineEdit(window)
    line.setText(line_text)
    line.setStyleSheet("font:15pt")
    label.setFont(font)
    line.resize(int(190*scale_rate), int(25*scale_rate))
    line.move(int((x_pos+127)*scale_rate),int((y_pos-1)*scale_rate))
    return line

def add_label_line2(window, scale_rate, x_pos=23, y_pos=16, label_text='', line_text=''):
    label=QLabel(window)
    label.setText(label_text)
    label.setStyleSheet("font:10pt")
    label.setFont(font)
    label.resize(label.sizeHint())
    label.move(int(x_pos*scale_rate), int(y_pos*scale_rate))
    line=QLineEdit(window)
    line.setText(line_text)
    line.setStyleSheet("font:10pt")
    label.setFont(font)
    line.resize(int(180*scale_rate), int(17*scale_rate))
    line.move(int((x_pos+90)*scale_rate),int((y_pos+1)*scale_rate))
    # line.setDisabled(True)
    return line

def add_comb_line(window, scale_rate, x_pos=23, y_pos=16, label_text='', line_text=0, line_text_list=['']):
    label=QLabel(window)
    label.setText(label_text)
    label.setStyleSheet("font:15pt")
    label.setFont(font)
    label.resize(label.sizeHint())
    label.move(int(x_pos*scale_rate), int(y_pos*scale_rate))
    line=QComboBox(window)
    line.addItems(line_text_list)
    line.setCurrentIndex(line_text) 
    line.setStyleSheet("font:15pt")
    label.setFont(font)
    line.resize(int(190*scale_rate), int(25*scale_rate))
    line.move(int(150*scale_rate),int((y_pos-2)*scale_rate))
    return line

class EmittingStr(QtCore.QObject):
    textWritten = QtCore.pyqtSignal(str)  # 定义一个发送str的信号

    def write(self, text):
        self.textWritten.emit(str(text))
    def flush(self):
        pass
    def isatty(self):
        pass

default_setting = {'WiFi名称:':'', 'WiFi密码:':'', '城市拼音:':'Beijing', '天气秘钥:':'', '屏幕亮度:':'60', 'CPU性能:':'中', '自动连WiFi:':'否'}

str_dict = {'WiFi名称:':'ssid', 'WiFi密码:':'password', '城市拼音:':'cityname', '天气秘钥:':'key', '屏幕亮度:':'lum', 'CPU性能:':'power', '自动连WiFi:':'autoconnect', '低':"low", '中':'medium', '高':'high', '是':'true', '否':'false'}

def read_ser(window, ser):
    # stra = ser.readline()
        while(window.is_connected):
            stra = ser.readline()
            if len(stra)>0:
                print(str(stra)[2:-5])
class First_window(QMainWindow):
    def __init__(self, scale_rate):
        super().__init__()
        self.setWindowIcon(QIcon('./sdk/poki.ico'))
        
        # self.main_core=main_core
        set_color(self)
        self.scaleRate = scale_rate
        window_width,window_height=int(710*self.scaleRate),int(425*self.scaleRate)
        screen_center(self,window_width,window_height)
        self.setWindowTitle("Poki for Holo设置PC客户端")
        self.status=self.statusBar()
        self.status.showMessage("没有连接任何设备!")
        self.status.setStyleSheet("font:15pt")
        self.is_connected = False

        
        # self.add_label_editline()
        self.add_button()
        self.label_text_list= ['WiFi名称:', 'WiFi密码:', '城市拼音:', '天气秘钥:']
        self.comb_text_list = ['屏幕亮度:', 'CPU性能:', '自动连WiFi:']
        self.line_dict = {}
        self.comb_text_list_list = [['10', '20', '30', '40', '50', '60', '70', '80', '90', '100'], ['低', '中', '高'], ['是', '否']]
        ports = list(serial.tools.list_ports.comports())
        self.ports = [str(port).split(' ')[0] for port in ports]

        if os.path.exists('./sdk/para.json'):
            f = open('./sdk/para.json', 'r')
            self.set_dict = json.load(f)
            f.close()
        else:
            self.set_dict = default_setting
            f = open('./sdk/para.json', 'w')
            json.dump(self.set_dict, f)
            f.close()
        self.add_editline()

        self.textBrowser = QtWidgets.QTextBrowser(self)
        self.textBrowser.move(int(360*scale_rate), int(250*scale_rate))
        self.textBrowser.resize(int(340*scale_rate), int(167*scale_rate))
        sys.stdout = EmittingStr(textWritten=self.outputWritten)
        sys.stderr = EmittingStr(textWritten=self.outputWritten)

    def outputWritten(self, text):
        cursor = self.textBrowser.textCursor()
        cursor.movePosition(QtGui.QTextCursor.End)
        cursor.insertText(text)
        self.textBrowser.setTextCursor(cursor)
        self.textBrowser.ensureCursorVisible()

    def add_editline(self):

        self.com_comb = add_comb_line(self, self.scaleRate, x_pos=23, y_pos=10, label_text="串口号:", line_text=0, line_text_list=self.ports)

        for i, label_text in enumerate(self.label_text_list):
            line = add_label_line(self, self.scaleRate, x_pos=23, y_pos=50+i*40, label_text=label_text, line_text=self.set_dict[label_text])
            self.line_dict[label_text]=line

        for i, label_text in enumerate(self.comb_text_list):
            line_text_list = self.comb_text_list_list[i]
            line_text = line_text_list.index(self.set_dict[label_text])
            line = add_comb_line(self, self.scaleRate, x_pos=23, y_pos=50 + 4*40+ i*40, label_text=label_text, line_text=line_text,line_text_list=line_text_list)
            self.line_dict[label_text]=line
        
        self.file_bin_line = add_label_line2(self, self.scaleRate, x_pos=360, y_pos=10, label_text='固件位置:', line_text='')
        self.file_mp4_line = add_label_line2(self, self.scaleRate, x_pos=360, y_pos=100, label_text='原始MP4位置:', line_text='')
        self.folder_mp4_line = add_label_line2(self, self.scaleRate, x_pos=360, y_pos=120, label_text='导出ANI位置:', line_text='')
        self.file_gif_line = add_label_line2(self, self.scaleRate, x_pos=360, y_pos=180, label_text='原始GIF位置:', line_text='')
        self.folder_gif_line = add_label_line2(self, self.scaleRate, x_pos=360, y_pos=200, label_text='导出ANI位置:', line_text='')

    def connect(self):
        rate = 115200
        port = self.com_comb.currentText()
        if len(port)>0 and not self.is_connected:
            try:
                self.ser = serial.Serial()
                self.ser.port = port
                self.ser.rate = rate
                self.ser.setDTR(True)
                self.ser.setRTS(False)
                self.ser.open()
                self.ser.close()
                self.ser = serial.Serial(port, rate, timeout=0.5)
                self.is_connected=True
                self.status.showMessage("已经连接:"+port)
                self.thread_ser = threading.Thread(target=read_ser, args=(self, self.ser))
                self.thread_ser.start()
            except Exception as e:
                print(e)
                self.status.showMessage("连接失败!")
    
    
    def refresh(self):
        if self.is_connected:
            self.ser.close()
            self.is_connected = False
        self.status.showMessage("没有连接任何设备!")
        self.com_comb.clear()
        ports = list(serial.tools.list_ports.comports())
        
        self.ports = [str(port).split(' ')[0] for port in ports]
        self.com_comb.addItems(self.ports)
        self.com_comb.setCurrentIndex(0)

    def clear_box(self):
        self.textBrowser.clear()
        
    def add_button(self):

        btn=QPushButton('提交\n(ENTER)', self)
        btn.setStyleSheet("font:15pt;background-color:rgb(152,245,255);")
        btn.setFont(font)
        btn.clicked.connect(self.send_para)
        btn.resize(int(150*self.scaleRate), int(50*self.scaleRate))
        btn.move(int(200*self.scaleRate),int(330*self.scaleRate))
        btn.setShortcut(Qt.Key_Return)#设置回车快捷键

        btn=QPushButton('连接', self)
        btn.setStyleSheet("font:15pt;background-color:rgb(152,245,255);")
        btn.setFont(font)
        btn.clicked.connect(self.connect)
        btn.resize(int(100*self.scaleRate), int(25*self.scaleRate))
        btn.move(int(30*self.scaleRate),int(325*self.scaleRate))

        btn=QPushButton('清屏', self)
        btn.setStyleSheet("font:15pt;background-color:rgb(152,245,255);")
        btn.setFont(font)
        btn.clicked.connect(self.clear_box)
        btn.resize(int(100*self.scaleRate), int(25*self.scaleRate))
        btn.move(int(240*self.scaleRate),int(392*self.scaleRate))

        btn=QPushButton('刷新/断开', self)
        btn.setStyleSheet("font:15pt;background-color:rgb(152,245,255);")
        btn.setFont(font)
        btn.clicked.connect(self.refresh)
        btn.resize(int(100*self.scaleRate), int(25*self.scaleRate))
        btn.move(int(30*self.scaleRate),int(365*self.scaleRate))

        btn=QPushButton('浏览', self)
        btn.setStyleSheet("font:9pt;background-color:rgb(152,245,255);")
        btn.setFont(font)
        btn.clicked.connect(self.setbinpath)
        btn.resize(int(66*self.scaleRate), int(18*self.scaleRate))
        btn.move(int(635*self.scaleRate),int(10*self.scaleRate))

        btn=QPushButton('浏览', self)
        btn.setStyleSheet("font:9pt;background-color:rgb(152,245,255);")
        btn.setFont(font)
        btn.clicked.connect(self.setmp4path)
        btn.resize(int(66*self.scaleRate), int(18*self.scaleRate))
        btn.move(int(635*self.scaleRate),int(100*self.scaleRate))

        btn=QPushButton('浏览', self)
        btn.setStyleSheet("font:9pt;background-color:rgb(152,245,255);")
        btn.setFont(font)
        btn.clicked.connect(self.setmp4folder)
        btn.resize(int(66*self.scaleRate), int(18*self.scaleRate))
        btn.move(int(635*self.scaleRate),int(120*self.scaleRate))

        btn=QPushButton('浏览', self)
        btn.setStyleSheet("font:9pt;background-color:rgb(152,245,255);")
        btn.setFont(font)
        btn.clicked.connect(self.setgifpath)
        btn.resize(int(66*self.scaleRate), int(18*self.scaleRate))
        btn.move(int(635*self.scaleRate),int(180*self.scaleRate))

        btn=QPushButton('浏览', self)
        btn.setStyleSheet("font:9pt;background-color:rgb(152,245,255);")
        btn.setFont(font)
        btn.clicked.connect(self.setgiffolder)
        btn.resize(int(66*self.scaleRate), int(18*self.scaleRate))
        btn.move(int(635*self.scaleRate),int(200*self.scaleRate))


        btn=QPushButton('清空Flash', self)
        btn.setStyleSheet("font:9pt;background-color:rgb(152,245,255);")
        btn.setFont(font)
        btn.clicked.connect(self.clear_flash)
        btn.resize(int(66*self.scaleRate), int(18*self.scaleRate))
        btn.move(int(400*self.scaleRate),int(50*self.scaleRate))


        btn=QPushButton('刷写', self)
        btn.setStyleSheet("font:9pt;background-color:rgb(152,245,255);")
        btn.setFont(font)
        btn.clicked.connect(self.burn_flash)
        btn.resize(int(66*self.scaleRate), int(18*self.scaleRate))
        btn.move(int(635*self.scaleRate),int(50*self.scaleRate))

        btn=QPushButton('重启', self)
        btn.setStyleSheet("font:9pt;background-color:rgb(152,245,255);")
        btn.setFont(font)
        btn.clicked.connect(self.reboot)
        btn.resize(int(66*self.scaleRate), int(18*self.scaleRate))
        btn.move(int(515*self.scaleRate),int(50*self.scaleRate))

        btn=QPushButton('转换MP4', self)
        btn.setStyleSheet("font:9pt;background-color:rgb(152,245,255);")
        btn.setFont(font)
        btn.clicked.connect(self.transfer_mp4)
        btn.resize(int(66*self.scaleRate), int(18*self.scaleRate))
        btn.move(int(550*self.scaleRate),int(140*self.scaleRate))

        btn=QPushButton('转换GIF', self)
        btn.setStyleSheet("font:9pt;background-color:rgb(152,245,255);")
        btn.setFont(font)
        btn.clicked.connect(self.transfer_gif)
        btn.resize(int(66*self.scaleRate), int(18*self.scaleRate))
        btn.move(int(550*self.scaleRate),int(220*self.scaleRate))

    def send_para(self):
        if self.is_connected:
            send_str = self.get_string()
            self.ser.write(send_str.encode(encoding = "utf-8"))
            self.save_para()
        else:
            self.status.showMessage("提交失败没有连接！")
            

    def get_string(self):
        for label_text in self.label_text_list:
            self.set_dict[label_text] = self.line_dict[label_text].text()
        for label_text in self.comb_text_list:
            self.set_dict[label_text] = self.line_dict[label_text].currentText()
        send_str = ""
        for label_text in self.label_text_list:
            send_str += "#key#" + str_dict[label_text] + "#value#" + self.set_dict[label_text] + "\0"
        for label_text in self.comb_text_list:
            if label_text=='屏幕亮度:':
                send_str += "#key#" + str_dict[label_text] + "#value#" + self.set_dict[label_text] + "\0"
            else:
                send_str += "#key#" + str_dict[label_text] + "#value#" + str_dict[self.set_dict[label_text]] + "\0"
        return send_str
        
    def save_para(self):
        f = open('./sdk/para.json', 'w')
        json.dump(self.set_dict, f)
        f.close()

    def setbinpath(self): 
        bin_path = QFileDialog.getOpenFileName(self, "选取文件", os.getcwd(), 
        "Bin Files(*.bin)")[0]
        if len(bin_path)>0:
            self.file_bin_line.setText(bin_path)

    
    def setmp4path(self): 
        bin_path = QFileDialog.getOpenFileName(self, "选取文件", None, 
        "MP4 Files(*.mp4)")[0]
        if len(bin_path)>0:
            self.file_mp4_line.setText(bin_path)

    def setgifpath(self): 
        bin_path = QFileDialog.getOpenFileName(self, "选取文件", None, 
        "GIF Files(*.gif)")[0]
        if len(bin_path)>0:
            self.file_gif_line.setText(bin_path)

    def setmp4folder(self):
        path = QFileDialog.getExistingDirectory()
        if len(path)>0:
            self.folder_mp4_line.setText(path)
    
    def setgiffolder(self):
        path = QFileDialog.getExistingDirectory() 
        if len(path)>0:
            self.folder_gif_line.setText(path)




    def clear_flash(self):
        if self.is_connected:
            self.ser.close()
            self.is_connected = False
        port = self.com_comb.currentText()
        command = ["--chip", "esp32", "--port", port, "--baud", "115200", "erase_flash"]
        self.status.showMessage("清除中......")
        self.thread = threading.Thread(target=clear_flash_fun, args=(command, self.status))
        self.thread.start()
        
    def burn_flash(self):
        if self.is_connected:
            self.ser.close()
            self.is_connected = False
        port = self.com_comb.currentText()
        firmware_path = self.file_bin_line.text()
        command = ["--chip", "esp32", "--port", port, "--baud", "921600", "--before", "default_reset", "--after", "hard_reset", "write_flash", "-z", "--flash_mode", "dio", "--flash_freq", "40m", "--flash_size", "detect", '0x1000', './sdk/bootloader_dio_40m.bin', '0x10000', firmware_path, '0x8000', './sdk/partitions.bin']
        self.status.showMessage("烧录固件中......")
        self.thread = threading.Thread(target=burn_flash_fun, args=(command, self.status))
        self.thread.start()

    def reboot(self):
        if self.is_connected:
            self.ser.close()
            self.is_connected = False
        port = self.com_comb.currentText()
        command = ['--port', port, '--baud', "115200",
               '--after', 'hard_reset', "read_mac"]
        self.status.showMessage("重启设备中......")
        self.thread = threading.Thread(target=reboot_fun, args=(command, self.status))
        self.thread.start()

    def transfer_gif(self):
        infile = outfolder = ""
        try:
            infile = self.file_gif_line.text()
            outfolder = self.folder_gif_line.text()
            self.thread = threading.Thread(target=trans_gif, args=(infile, outfolder, self.status))
            self.thread.start()
        except Exception as e:
            print(e)
            self.status.showMessage("请选择正确路径！")
        
    def transfer_mp4(self):
        infile = outfolder = ""
        try:
            infile = self.file_mp4_line.text()
            outfolder = self.folder_mp4_line.text()
            self.thread = threading.Thread(target=trans_mp4, args=(infile, outfolder, self.status))
            self.thread.start()
        except Exception as e:
            print(e)
            self.status.showMessage("请选择正确路径！")

if __name__=='__main__':
    app=QApplication(sys.argv)
    scale_rate = app.screens()[0].logicalDotsPerInch()/96
    main_window=First_window(scale_rate)
    main_window.show()
    sys.exit(app.exec_())